/**
 * 
 */
package com.tcl.wip.client.query;

import java.util.ArrayList;
import java.util.List;

import com.tcl.wip.client.repository.SimpleWipRepository;


/**
 * @author zhaowen.zhuang
 * @Date Jan 19, 2015
 */
public class QueryBuilder<E, SK> {


    private List<Restriction> restrictions;

    private OrderExpression[] orderExpressions;

    private StringBuilder whereStatement = new StringBuilder();

    private Column<SK> shardingColumn;

    private SK shardingKey;

    private SimpleWipRepository<?, ?, ?> simpleRepository;


    public QueryBuilder(SimpleWipRepository<?, ?, ?> simpleRepository) {
        this.simpleRepository = simpleRepository;
        restrictions = new ArrayList<Restriction>();
    }

    public QueryBuilder<E, SK> withShardingKey(Column<SK> shardingColumn, SK sk) {
        this.shardingColumn = shardingColumn;
        this.shardingKey = sk;
        this.and(shardingColumn.eq(sk));
        return this;
    }

    public QueryBuilder<E, SK> and(Expression expression) {
        restrictions.add(new Restriction(Restriction.AND, expression));
        return this;
    }

    public QueryBuilder<E, SK> or(Expression expression) {
        restrictions.add(new Restriction(Restriction.OR, expression));
        return this;
    }

    public QueryBuilder<E, SK> orderBy(OrderExpression... orderExpressions) {
        this.orderExpressions = orderExpressions;
        return this;
    }

    public List<E> list() {
        return (List<E>) this.simpleRepository.list(this.build());
    }

    public List<E> listPage(int offset, int limit) {
        return (List<E>) this.simpleRepository.listPage(this.build(), offset, limit);
    }

    public int count() {
        return this.simpleRepository.count(this.build());
    }

    public E singleResult() {
        List<E> list = (List<E>) this.simpleRepository.listPage(this.build(), 0, 1);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    public int delete() {
        return this.simpleRepository.delete(this.build());
    }

    public String toSql() {
        Query<?> query = this.build();
        return query.getWhere();
    }

    public Query build() {
        if (this.shardingColumn == null && this.shardingKey == null) {
            throw new RuntimeException("query must contain sharding key.");
        }
        StringBuilder whereStatement = new StringBuilder();
        List<Parameter<?>> parameters = new ArrayList<>();
        whereStatement.append(" where ")
                .append(restrictions.get(0).getExpression().toSqlFragment());
        restrictions.get(0).getExpression().appendParameterTo(parameters);
        if (restrictions.size() > 1) {
            for (int i = 1; i < restrictions.size(); i++) {
                Restriction restriction = restrictions.get(i);
                whereStatement.append(restriction.getOp()).append(
                        restriction.getExpression().toSqlFragment());
                restriction.getExpression().appendParameterTo(parameters);

            }
        }

        if (this.orderExpressions != null && this.orderExpressions.length > 0) {
            whereStatement.append(" order by ").append(orderExpressions[0].toSqlFragment());
            if (orderExpressions.length > 1) {
                for (int i = 1; i < orderExpressions.length; i++) {
                    whereStatement.append(",").append(orderExpressions[i].toSqlFragment());
                }
            }
        }
        return new Query<SK>(this.shardingKey, whereStatement.toString(), parameters);
    }

}
